package main

import (
	"net"
	"os"
	"server_cluster/common/config"
	"server_cluster/common/gdconf"
	"server_cluster/common/logger"
	"server_cluster/common/myepoll"
	"server_cluster/common/mysock"
	"server_cluster/common/sharedb"
	"server_cluster/common/tools"
	"server_cluster/gate/db"
	"server_cluster/gate/gameLogic"
	"server_cluster/gate/gate_config"
	"server_cluster/gate/sharemem"
	"strconv"
	"sync"
	"time"
)

var wg sync.WaitGroup

func handleConn() {
	wg.Add(1)
	defer wg.Done()
	eps := &myepoll.Eps
	var result uint16
	var myshm = sharemem.Myshm
	var shm_user *sharemem.User
	var timeout int64
	mybuf := mysock.Create()
	next_time := myshm.Time_now + gate_config.USER_CHECK_TIME

	for myshm.Status == 1 || myshm.Status == 2 {
		// 超时判断 | 统一广播
		// sleep 0.2s
		// time.Sleep(2 * 100 * time.Millisecond)
		eps.Fds.Range(func(key, value interface{}) bool {
			v, ok := value.(*(myepoll.Conn))
			if ok {
				switch v.Type {
				case myepoll.CONN_TYPE_CLIENT:
					if v.Timeout < myshm.Time_now || v.Total > gate_config.CLIENT_SEND_LIMIT {
						// 超时判断
						logger.Warn("socket %v is timeout or send limit %d...%s", key, v.Total, v.Socket.RemoteAddr())
						gameLogic.CleanClient(key.(int32))
					}
				default:
					if v.Timeout < myshm.Time_now {
						// 超时判断
						logger.Warn("server socket %v is timeout...%s", key, v.Socket.RemoteAddr())
						eps.CleanServerConn(v.Type, v.Sid)
					}
					// 内部连接
				}
				// 统一广播 to do ...
				//
			}
			return true
		})
		if myshm.Time_now > next_time {
			next_time += gate_config.USER_CHECK_TIME
			gameLogic.UserCheck()
		}
		// 2秒
		es, err := eps.Wait(2000)
		if err != nil {
			continue
		}
		for _, e := range es {
			conns := eps.GetConnByFd(e.Fd)
			if conns == nil {
				logger.Error("%d map not found.", e.Fd)
				continue
			}
			// 有消息
			shm_user = nil
			switch conns.Type {
			case myepoll.CONN_TYPE_CLIENT: // client
				result = mybuf.ReadFromOutSide(conns.Socket)
				if result != config.OK {
					if result == config.TIMEOUT {
						continue
					}
					logger.Warn("%d connection is broken. %s", result, conns.Socket.RemoteAddr())
					gameLogic.CleanClient(e.Fd)
					continue
				}
				logger.Info("Gate read from Client:Command %d Session=%d Code=%d,Data_length = %d Ext=%d IP=%s", mybuf.Command, mybuf.Session, mybuf.Code, mybuf.Data_length, mybuf.Ext, conns.Socket.RemoteAddr().String())
				result = gameLogic.Action(conns.Socket, mybuf, &shm_user, e.Fd)
				if result != config.OK {
					mybuf.Command = config.SC_ERROR
					mybuf.Code = result
					mybuf.WriteToByConn(conns.Socket, nil)
					logger.Warn("%d gameLogic action error.", result)
					continue
				}
				if shm_user != nil {
					if conns.Site != shm_user.Index {
						conns.Site = shm_user.Index
					}
					shm_user.AliveTime = myshm.Time_now
				}
				conns.Total++
				timeout = myshm.Time_now + gate_config.CLIENT_TIME_OUT_MAX
			default: // server
				result = mybuf.ReadFrom(conns.Socket)
				logger.Info("Gate read from Server %d:Command %d Session=%d Code=%d,Data_length = %d Ext=%d IP=%s", conns.Type, mybuf.Command, mybuf.Session, mybuf.Code, mybuf.Data_length, mybuf.Ext, conns.Socket.RemoteAddr().String())
				if result != config.OK {
					if result == config.TIMEOUT {
						continue
					}
					logger.Warn("server connection is broken. %s result=%d", conns.Socket.RemoteAddr(), result)
					eps.CleanServerConn(conns.Type, conns.Sid)
					continue
				}
				gameLogic.ServerAction(conns.Socket, mybuf, conns.Type, conns.Sid)
				timeout = myshm.Time_now + config.TIME_OUT_MAX
			}

			if timeout > conns.Timeout {
				conns.Timeout = timeout
				conns.Total = 0
			}
			eps.Fds.Store(e.Fd, conns)
			//
		}
		//
	}
	// 停服 全部强制断开连接
	eps.Fds.Range(func(key, value interface{}) bool {
		v, ok := value.(*(myepoll.Conn))
		if ok {
			logger.Warn("close socket %v...%s", key, v.Socket.RemoteAddr())
			switch v.Type {
			case myepoll.CONN_TYPE_CLIENT:
				gameLogic.CleanClient(key.(int32))
			default:
				eps.CleanServerConn(v.Type, v.Sid)
			}
			//
		}
		return true
	})
}

func listenAccept() {
	logger.Warn("listen...")
	listener, err := net.Listen("tcp", gate_config.CONF.GAMEHOST)
	if err != nil {
		logger.Error("%v", err)
		os.Exit(0)
	}
	eps := &myepoll.Eps
	myshm := sharemem.Myshm

	logger.Warn("Server started.Listen on %s", gate_config.CONF.GAMEHOST)
	var nfd int32
	for myshm.Status == 1 {
		// 此处会阻塞
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		rawConn, err := conn.(*net.TCPConn).SyscallConn()
		if err != nil {
			continue
		}
		rawConn.Control(func(fd uintptr) {
			nfd = int32(fd)
		})
		if myshm.Status != 1 {
			conn.Close()
			break
		}
		err = eps.AddFd(nfd, conn.(*net.TCPConn), myshm.Time_now, myepoll.CONN_TYPE_CLIENT, 0, 0)
		if err != nil {
			conn.Close()
			continue
		}
		logger.Warn("%s connect success %d", conn.RemoteAddr().String(), nfd)
	}
	logger.Warn("listen accept stop ...")
	listener.Close()
}

func syncServer() {
	defer func() {
		if err := recover(); err != nil {
			logger.Error("!!! Server action LOOP PANIC !!!")
			logger.Error("error: %v", err)
			logger.Error("stack: %v", logger.Stack())
		}
	}()
	var nfd int32
	cfg := sharedb.Config_server{}
	data, err := cfg.GetAllWithOutGate(db.Conn)
	if err != nil {
		logger.Error("get all with out gate error.", err)
		return
	}
	eps := &myepoll.Eps
	for _, value := range data {
		v, ok := eps.GetServerMapByType(value.Type)[value.Id]
		if ok {
			// 已存在 更新库存
			v.Stock = value.Stock
			eps.SetServerMapStock(value.Type, value.Id, value.Stock)
			continue
		}
		// 不存在 进行初始化
		conn, err := net.Dial("tcp", *(value.IpLan)+":"+strconv.Itoa(int(value.Port)))
		if err != nil {
			logger.Error("dial error.", err)
			continue
		}
		rawConn, err := conn.(*net.TCPConn).SyscallConn()
		if err != nil {
			logger.Error("syscall conn error.", err)
			continue
		}
		rawConn.Control(func(fd uintptr) {
			nfd = int32(fd)
		})
		if value.Type == myepoll.CONN_TYPE_CHAT_ROOT {
			err = eps.AddFd(nfd, conn.(*net.TCPConn), sharemem.Myshm.Time_now, value.Type, 0, value.Stock)
		} else {
			err = eps.AddFd(nfd, conn.(*net.TCPConn), sharemem.Myshm.Time_now, value.Type, value.Id, value.Stock)
		}
		if err != nil {
			logger.Error("add fd error.", err)
			conn.Close()
			continue
		}
		logger.Warn("server %s connect success %d", conn.RemoteAddr().String(), nfd)
	}
}

// 后端服务
func backServer() {
	logger.Warn("back server start...")
	myshm := sharemem.Myshm
	sync_time := myshm.Time_now
	for {
		if myshm.Time_now >= sync_time {
			// 60秒同步一次
			sync_time = myshm.Time_now + 60
			syncServer()
			gameLogic.SyncStock()
		}
		time.Sleep(1 * time.Second)
	}
}

func main() {
	gate_config.InitConfig()
	logger.InitLogger("gate-server_"+strconv.Itoa(gate_config.GetConfig().SERVER_ID), gate_config.GetConfig().LOG_LEVEL)
	defer logger.CloseLogger()
	shmid := sharemem.Get_myshm(&(sharemem.Myshm))
	if shmid <= 0 {
		logger.Error("init myshm fail.")
		return
	}
	myshm := sharemem.Myshm
	if myshm.Status != 0 {
		logger.Error("game service is not init.")
		return
	}
	sharemem.Init_all_user_index()
	sharemem.InitAllUserFd()
	err := db.InitDB()
	if err != nil {
		logger.Error("init db fail.", err)
		return
	}
	defer func() {
		myshm.Status = 0
	}()
	myshm.Status = 1
	myshm.Time_now = tools.TimeNow()

	// 初始化epoll
	eps := &myepoll.Eps
	err = eps.InitServer(gate_config.SHM_USER_MAX)
	if err != nil {
		logger.Error("%v", err)
		return
	}

	start_time := gdconf.GetServerStartTime(db.Conn)
	if start_time <= 0 {
		logger.Error("GetServerStartTime error.")
		//		return
	}

	logger.Warn("game start time = %v", start_time)
	for myshm.Status == 1 {
		if myshm.Time_now >= start_time {
			break
		}
		time.Sleep(1 * time.Second)
		myshm.Time_now = tools.TimeNow()
	}

	go backServer()
	go listenAccept()
	go handleConn()

	logger.Warn("server start...")
	cfg_server := sharedb.Config_server{}
	if !cfg_server.Update_server_status(db.Conn, int16(gate_config.CONF.SERVER_ID), int8(1)) {
		logger.Error("update server status error.")
		return
	}
	for myshm.Status == 1 {
		time.Sleep(1 * time.Second)
		myshm.Time_now = tools.TimeNow()
		// log.Println(myshm.Time_now,tools.Time2str(myshm.Time_now))
	}
	logger.Warn("server stop...status=%d", myshm.Status)
	cfg_server.Update_server_status(db.Conn, int16(gate_config.CONF.SERVER_ID), int8(0))
	for i := 3; i >= 0; i-- {
		logger.Warn("%d", i)
		time.Sleep(1 * time.Second)
	}
	myshm.Status = 3
	logger.Warn("main wait...")
	wg.Wait()
	logger.Warn("main stop ...")
	myshm.Status = 0
	logger.Warn("server stop.")
}
